{# Layout macros: the pieces that frame a page rather than fill it. Import what you need: {% from "components/macros/layout.html" import card, stat_tile, dialog, dropdown %} #} {% from "components/macros/feedback.html" import empty_state %} {# A titled surface. The body comes via {% call %}; the heading id ties the section to its title for assistive tech. ``id`` lets a page target the card with htmx; it defaults to a slug of the title. #} {% macro card(title=None, subtitle=None, id=None) %} {% set card_id = id or ("card-" ~ title|lower|replace(" ", "-")) %}
{# A card whose page title already names it (the forecast's chart) goes without a header rather than repeating the words. #} {% if title %}

{{ title }}

{% if subtitle %}

{{ subtitle }}

{% endif %}
{% endif %} {{ caller() }}
{% endmacro %} {# One headline figure. Render a row of them inside a ``
``. Red once a figure goes negative and nothing otherwise: when every healthy number is coloured, colour stops meaning anything. #} {% macro stat_tile(label, value, negative=False, caption=None, attrs="") %}
{{ label }}
{{ value }}
{% if caption %}
{{ caption }}
{% endif %}
{% endmacro %} {# A thin native progress bar; ``tone`` colours it (see badge). #} {% macro progress(ratio, tone="ok") %} {% set tones = {"ok": "accent-aegis-teal", "warn": "accent-aegis-amber", "error": "accent-aegis-error", "muted": "accent-aegis-muted"} %} {% endmacro %} {# A chart card (JS island #1). The canvas declares its kind and points at a JSON data block; static/js/charts.js mounts Chart.js over every ``canvas[data-chart]`` on load and after each htmx settle. Data shape: ``{"labels": [...], "series": [{"label": ..., "values": [...]}], "slices": [{"categories": [...]}]}`` (``slices`` only for drilldowns). ``drilldown`` is the URL a slice click loads into the dialog; the slice's categories are appended as ``category=`` params. #} {% macro chart_panel(id, title, kind, data, drilldown=None, subtitle=None) %} {# ``title`` may be ``None``: the canvas keeps its aria-label either way. #} {% call card(title, subtitle, id="chart-" ~ id) %}
{% endcall %} {% endmacro %} {# The one modal. Native : focus trap, Escape and backdrop come free. Content is loaded by htmx into ``#dialog-body`` (pattern 4); app.js opens the dialog when a swap lands there, and closing clears it so a stale body never flashes on the next open. Mounted once by the app shell, outside ``#app-content``. #} {% macro dialog() %}
{% endmacro %} {# Switches between the theme blocks in static/input.css (see static/js/theme.js). Drop it into any navbar or sidebar. #} {% macro theme_toggle() %} {% endmacro %} {# Full-bleed dismiss layer behind a modal. Pair it with the modal panel:
{{ modal_scrim("close()") }}
...
#} {% macro modal_scrim(dismiss_handler="open = false") %}
{% endmacro %} {# THE tooltip panel — the one popover shell every hover/help hint uses. Callers provide the open/show()/hide() Alpine state on a ``relative`` wrapper and pass the body via {% call %}. Change tooltip styling here, nowhere else. #} {% macro popover_panel(align='left') %} {% endmacro %} {# Inline definition hint — wrap a term via {% call %}; hovering (or tapping) opens the shared popover panel. Dotted underline signals the affordance without a "?" button. ``align='right'`` for terms near the right edge. #} {% macro hover_hint(title, text, align='left') %} {{ caller() }} {% call popover_panel(align) %}
{{ title }}
{{ text }}
{% endcall %}
{% endmacro %} {# Small ? info icon with a click-triggered popover. Body can be arbitrary HTML (caller passes it as the macro body via {% call %}). #} {% macro info_tooltip(width="w-64") %}
{{ caller() }}
{% endmacro %} {# A small status word with a dot: ``tone`` is ok, warn, error, muted or accent; no tone renders the label plain. One shape for every "state" cell and chip, so tests find them by role. #} {% macro badge(label, tone=None) %} {% set tones = {"ok": "text-aegis-teal", "warn": "text-aegis-amber", "error": "text-error", "muted": "text-aegis-muted", "accent": "text-aegis-accent"} %} {% if tone %}{% endif %}{{ label }} {% endmacro %} {# One entry in a dropdown() menu. ``attrs`` carries the htmx verb (``hx-get``/``hx-post``/``hx-delete`` plus target and swap); the menu closes itself on the click, so a dialog never opens over it. #} {% macro menu_item(label, attrs, danger=False) %}
  • {% endmacro %} {% macro dropdown(label, align="right") %}
    {{ label }}
    {% endmacro %} {# A confirmation as dialog body: what will happen, one destructive action (``method`` is delete or post to ``url``), and a way out. The action's response closes the dialog and navigates or swaps as it sees fit; nothing swaps here (hx-swap="none"). #} {# A dialog's heading: what this is, and the one line under it. ``subtitle`` is a sentence - what the dialog is for, or what it will do. ``facts`` is a metadata line (kind, date, size) and is set in the label voice instead. A ``{% call %}`` block supplies that line itself when it carries markup. The margin belongs here because it depends on whether anything follows: a title with a line under it sits close to it, a title alone stands off from the form below. Written out at each dialog it drifts - downstream it had reached four different answers across thirty-five. #} {% macro dialog_title(title, subtitle=None, facts=None) %} {%- set under = subtitle or facts or caller is defined %}

    {{ title }}

    {%- if facts %}

    {{ facts }}

    {%- elif subtitle %}

    {{ subtitle }}

    {%- elif caller is defined %} {{ caller() }} {%- endif %} {% endmacro %} {% macro confirm(title, body, url, label="Remove", method="delete") %} {{ dialog_title(title, body) }}
    {% endmacro %} {# The page header, dashboard shape: title and subtitle on the left, and whatever the caller puts on the right (headline ``figures``, chips, actions) in the same row. Every section opens with one. #} {% macro page_header(title, subtitle=None) %}

    {{ title }}

    {% if subtitle %}

    {{ subtitle }}

    {% endif %}
    {{ caller() if caller is defined }}
    {% endmacro %} {# Headline figures for a header: label over a large number, right-aligned. ``items`` are ``{"label", "value", "negative"}``; ``negative`` colours the number, nothing else does. #} {% macro figures(items) %}
    {% for item in items %}
    {{ item.label }}
    {{ item.value }}
    {% endfor %}
    {% endmacro %} {# One bordered strip of N cells (the Budget's month equation). Each cell is ``{"label", "value", "caption", "tone", "attrs"}``; ``attrs`` makes it a door (hx-get) and ``tone`` colours the value. #} {% macro stats_strip(cells, id=None, attrs="") %} {# ``lg:grid-cols-N`` is looked up, never interpolated: Tailwind emits the classes it can SEE in the markup, and a class built at render time is not one of them - ``lg:grid-cols-{{ cells|length }}`` compiled to nothing and every strip stayed two columns at every width. #}
    {% for cell in cells %}
    {# A figure that can explain itself has to LOOK like it does: the hover fill is only found by the mouse already on it. The mark is for ``explain`` alone, not for any clickable cell - a cell that filters a list is not offering to define a word. #}
    {{ cell.label }} {% if cell.explain %}{% endif %}
    {{ cell.value }}
    {% if cell.caption %}
    {{ cell.caption }}
    {% endif %}
    {% endfor %}
    {% endmacro %} {# Ranked rows: name, a small count, the amount, and a thin bar scaled to the largest row (the Overview's payees and bills). ``rows`` are ``{"label", "count", "value", "ratio", "tone"}``. #} {% macro ranked_rows(rows) %}
      {% for row in rows %}
    1. {{ row.label }} {% if row.count %}{{ row.count }}{% endif %}{{ row.value }}
    2. {% endfor %}
    {% endmacro %} {# Tabs: sibling views of one page, Pulse's underline recipe (the same look Flet's tabs had). ``tab_bar`` is the row (``role="tablist"``); each ``tab_item`` is a link: ``href`` for a cold load, ``hx`` the htmx attributes (``hx_replace(...)`` or a plain string). #} {% macro tab_bar(label, id=None, attrs="") %} {% endmacro %} {% macro tab_item(label, active=False, href="", hx="") %} {{ label }} {% endmacro %} {# A chip: one of a row of exclusive choices (a time window, a month, a view). Pulse's date-range recipe, which the Flet range chips copied: small, outlined, a light teal fill when chosen. #} {% macro chip(label, active=False, href="", hx="", extra="") %} {{ label }} {% endmacro %}